package jmu.db.test.xtest.util ;


import java.sql.Connection ;
import java.sql.PreparedStatement ;
import java.sql.ResultSet ;
import java.sql.SQLException ;
import java.util.HashMap ;
import java.util.List ;
import java.util.Map ;

import jmu.db.test.xtest.TaskResult ;
import jmu.db.test.xtest.TestField ;
import jmu.db.test.xtest.TestTable ;
import jmu.db.test.xtest.TestTask ;
import jmu.db.test.xtest.common.Identity ;
import jmu.db.test.xtest.validator.Validator ;

public abstract class TaskHelper
{
    private Connection connection = null ;
    private final Map < String, PreparedStatement > statements = new HashMap < String, PreparedStatement > () ;

    public TaskHelper ( final Connection connection ) throws SQLException
    {
        setConnection ( connection ) ;
    }

    public abstract int createResult ( final Identity taskid ) throws SQLException ;

    public TestTask getTask ( final Identity taskid ) throws SQLException
    {
        return getTask ( taskid, false, false ) ;
    }

    public TestTask getTask ( final Identity taskid, final boolean tables, final boolean fields ) throws SQLException
    {
        final TestTask task = getTaskImpl ( taskid ) ;
        if ( tables )
        {
            task.addItems ( listTables ( taskid, fields ) ) ;
        }

        return task ;
    }

    public abstract List < TestField > listFields ( final Identity tableid ) throws SQLException ;

    public List < TestTable > listTables ( final Identity taskid ) throws SQLException
    {
        return listTables ( taskid, false ) ;
    }

    public List < TestTable > listTables ( final Identity taskid, final boolean fields ) throws SQLException
    {
        final List < TestTable > tables = listTablesImpl ( taskid ) ;
        if ( fields )
        {
            for ( final TestTable table : tables )
            {
                table.addItems ( listFields ( table.getIdentity () ) ) ;
            }
        }

        return tables ;
    }

    public List < TestTask > listTasks () throws SQLException
    {
        return listTasks ( false, false ) ;
    }

    public List < TestTask > listTasks ( final boolean tables ) throws SQLException
    {
        return listTasks ( tables, false ) ;
    }

    public List < TestTask > listTasks ( final boolean tables, final boolean fields ) throws SQLException
    {
        final List < TestTask > tasks = listTasksImpl () ;
        if ( !tasks.isEmpty () && tables )
        {
            for ( final TestTask t : tasks )
            {
                listTables ( t.getIdentity (), fields ) ;
            }
        }
        return tasks ;
    }

    public abstract void updateResult ( final Identity taskid, TaskResult result ) throws SQLException ;

    protected Validator createValidator ( final String value )
    {
        if ( null != value )
        {
            try
            {
                final Class < ? > c = Class.forName ( value ) ;
                final Object o = c.newInstance () ;
                if ( o instanceof Validator )
                {
                    return (Validator) o ;
                }
            }
            catch ( final ClassNotFoundException e )
            {
                // ignore
            }
            catch ( final InstantiationException e )
            {
                // ignore
            }
            catch ( final IllegalAccessException e )
            {
                // ignore
            }
        }

        return null ;
    }

    @Override
    protected void finalize () throws Throwable
    {
        for ( PreparedStatement s : statements.values () )
        {
            try
            {
                if ( !s.isClosed () )
                {
                    s.close () ;
                }
            }
            catch ( SQLException e )
            {
                // ignore
            }
        }
        statements.clear () ;

        super.finalize () ;
    }

    protected Connection getConnection ()
    {
        return connection ;
    }

    protected PreparedStatement getPreparedStatement ( final String sql ) throws SQLException
    {
        // try get it from the cache
        PreparedStatement s = statements.get ( sql ) ;
        if ( null == s )
        {
            s = getConnection ().prepareStatement ( sql ) ;
            statements.put ( sql, s ) ;
        }

        return s ;
    }

    protected abstract TestTask getTaskImpl ( final Identity taskid ) throws SQLException ;

    protected abstract List < TestTable > listTablesImpl ( final Identity taskid ) throws SQLException ;

    protected abstract List < TestTask > listTasksImpl () throws SQLException ;

    protected TestField readField ( final ResultSet rs ) throws SQLException
    {
        final Identity id = readIdentity ( rs ) ;
        final TestField f = new TestField ( id ) ;
        f.setIgnored ( rs.getBoolean ( 4 ) ) ;
        f.setValidator ( createValidator ( rs.getString ( 5 ) ) ) ;

        return f ;
    }

    protected TestTable readTable ( final ResultSet rs ) throws SQLException
    {
        final Identity id = readIdentity ( rs ) ;

        final TestTable t = new TestTable ( id ) ;

        return t ;
    }

    protected TestTask readTask ( final ResultSet rs ) throws SQLException
    {
        final Identity id = readIdentity ( rs ) ;

        final TestTask t = new TestTask ( id ) ;
        t.setType ( rs.getString ( 3 ) ) ;

        return t ;
    }

    private Identity readIdentity ( final ResultSet rs ) throws SQLException
    {
        final Identity id = new Identity ( rs.getInt ( 1 ) ) ;
        id.setName ( rs.getString ( 2 ) ) ;
        return id ;
    }

    private void setConnection ( final Connection connection ) throws SQLException
    {
        if ( null == connection )
        {
            throw new IllegalArgumentException ( "connection can't be null" ) ;
        }
        if ( connection.isClosed () )
        {
            throw new IllegalArgumentException ( "connection is closed" ) ;
        }

        this.connection = connection ;
    }
}
